<?php
/**
 * @author Artur Bier <izatop@gmail.com>
 * @package Model
 *
 * @copyright TinyWEB CMS
 * @link http://tinywebcms.ru
 * @link http://cms.vingrad.ru
 * @version SVN: $Id$ 
 */
 
 /**
  * Model
  */
abstract class Model extends Object 
{
    /**
     * Хандлер драйвера базы данных.
     *
     * @var mysqli
     */
    private $handler;
    
    /**
     * Имя таблицы, которое будет использоваться в контексте модели.
     *
     * @var string
     */
    protected $table;

    /**
     * Текущее разрешение исполнения запросов на изменение данных.
     *
     * @var boolean
     */
    protected $writable;
    
    /**
     * Инициализация модели.
     *
     * @param string $place
     * @param boolean $writable
     */
    public function __construct($place = DbConnection::COMMON_SPACE, $writable = false)
    {
        $this->writable = $writable;
        $this->handler = DbConnection::get(DbConnection::COMMON_SPACE, $writable);
    }
    
    /**
     * Объявляет о начале транзакции в базе.
     *
     */
    final public function startTransaction()
    {
        $this->handler->query('START TRANSACTION');
    }
    
    /**
     * Откат транзакции.
     *
     */
    final public function rollback()
    {
        $this->handler->query('ROLLBACK');
    }
    
    /**
     * Подтверждение транзакции.
     *
     */
    final public function commit()
    {
        $this->handler->query('COMMIT');
    }
    
    /**
     * Выполнить SQL запрос.
     *
     * @param string $sql
     * @param DbQueryAnalyzer $QueryAnalyzer
     * @return resource
     */
    private function run($sql, DbQueryAnalyzer $QueryAnalyzer)
    {
        if($QueryAnalyzer->isWrite() && !$this->writable) {
            throw new Exception('Model: trying to modify data in readonly mode.');
        }
        
        $result = $this->handler->query($sql);
        if($this->handler->errno) {
            throw new Exception("Model: query error.\nQuery: ".$sql."\nError: ".$this->handler->errno . "\nMessage: ".$this->handler->error, $this->handler->errno);
        }

        return $result;
    }
    
    /**
     * Выполнить SQL запрос завернув результат в оболочку 
     * соответственного класса.
     *
     * @param string $sql
     * @return DbResult
     */
    public function query($sql)
    {
        $QueryAnayzer = new DbQueryAnalyzer($sql, $this->handler);
        return $QueryAnayzer->invoke($this->run($sql, $QueryAnayzer));
    }
    
    /**
     * Выполнить SQL запрос и вернуть его ресурс.
     *
     * @param string $sql
     * @return resource
     */
    public function exec($sql)
    {
        $QueryAnayzer = new DbQueryAnalyzer($sql, $this->handler);
        return $this->run($sql, $QueryAnayzer);
    }
    
    /**
     * Получить экземпляр класса DbQueryConstructor в контексте модели.
     *
     * @return DbQueryConstructor
     */
    public function getQueryConstructor()
    {
		return new DbQueryConstructor($this);
    }
    
    /**
     * Получить экземпляр класса DbStatement в контексте модели.
     *
     * @param string $sql
     * @return DbStatement
     */
    public function prepare($sql)
    {
    	return new DbStatement($this, $sql);
    }
    
    /**
     * Значение AUTO_INCREMENT последней вставки.
     *
     * @return int
     */
    public function getLastInsertId()
    {
        return $this->handler->insert_id;
    }
    
    /**
     * Возвращает имя теблицы в контексте модели.
     *
     * @return string
     */
    public function getTableName()
    {
    	return $this->table;
    }
    
    /**
     * Экранирование данных в запросе.
     *
     * @param string $string
     * @return string
     */
    public function escape($string)
    {
        return $this->handler->real_escape_string($string);
    }
}

/**
 * Tab: 4 spaces
 * Encoding: utf-8
 */
?>